#include "Lienzo.h"
#include "Cerdito.h"
#include "MonticuloHuevos.h"
#include "CajaVidrio.h"
#include "CajaMadera.h"
#include "CajaMetal.h"
#include "Manzana.h"
#include "Banana.h"
#include "Cereza.h"
#include <gtkmm/image.h>
#include <gtkmm/toolpalette.h>

Lienzo::LienzoItem::LienzoItem() { }

Lienzo::LienzoItem::LienzoItem(Gtk::ToolButton* boton, unsigned posX,
		unsigned posY) {
	crearUtilidad(boton->get_label());
	Gtk::Image *icono = dynamic_cast<Gtk::Image*>(boton->get_icon_widget());
	pixbuf = icono->get_pixbuf();
	pos_x = posX;
	pos_y = posY;
}

void Lienzo::LienzoItem::draw(const Cairo::RefPtr<Cairo::Context>& contexto,
		bool vistaPrevia) {
	/* Para que el agarre de la imagen sea del centro y no de la esquina
	 * superior izquierda de la imagen */
	const double cx = pixbuf->get_width();
	const double cy = pixbuf->get_height();
	Gdk::Cairo::set_source_pixbuf(contexto, pixbuf, pos_x - cx * 0.5,
			pos_y - cy * 0.5);

	/* Si es una vista previa, se pinta con transparencia */
	if (vistaPrevia) {
		contexto->paint_with_alpha(0.6);
	}
	else {
		contexto->paint();
	}
}

void Lienzo::LienzoItem::setPosX(unsigned posX) {
	pos_x = posX;
}

void Lienzo::LienzoItem::setPosY(unsigned posY) {
	pos_y = posY;
}

void Lienzo::LienzoItem::guardar(Escenario* escenario) {
	escenario->addUtilidad(utilidad->clonar(), new Posicion(pos_x, pos_y));
}

void Lienzo::LienzoItem::cargar(Utilidad* util) {
	utilidad = util->clonar();
	pos_x = utilidad->getPosicion()->X();
	pos_y = utilidad->getPosicion()->Y();
	Gtk::Image icono;

	if (utilidad->getRol()->Nombre() == ROL_CERDITO) {
		icono.set(RUTA_ICONO_CERDITO);
	}
	else if (utilidad->getRol()->Nombre() == ROL_MONTICULOHUEVOS) {
		icono.set(RUTA_ICONO_HUEVOS);
	}
	else if (utilidad->getRol()->Nombre() == ROL_CAJA_VIDRIO) {
		icono.set(RUTA_ICONO_CAJAVIDRIO);
	}
	else if (utilidad->getRol()->Nombre() == ROL_CAJA_MADERA) {
		icono.set(RUTA_ICONO_CAJAMADERA);
	}
	else if (utilidad->getRol()->Nombre() == ROL_CAJA_METAL) {
		icono.set(RUTA_ICONO_CAJAMETAL);
	}
	else if (utilidad->getRol()->Nombre() == ROL_MANZANA) {
		icono.set(RUTA_ICONO_MANZANA);
	}
	else if (utilidad->getRol()->Nombre() == ROL_BANANA) {
		icono.set(RUTA_ICONO_BANANA);
	}
	else if (utilidad->getRol()->Nombre() == ROL_CEREZA) {
		icono.set(RUTA_ICONO_CEREZA);
	}
	pixbuf = icono.get_pixbuf();
}

void Lienzo::LienzoItem::crearUtilidad(const Glib::ustring &nombre) {
	if (nombre == NOMBRE_CERDITO) {
		utilidad = new Cerdito;
	}
	if (nombre == NOMBRE_HUEVOS) {
		utilidad = new MonticuloHuevos;
	}
	if (nombre == NOMBRE_CAJAVIDRIO) {
		utilidad = new CajaVidrio;
	}
	if (nombre == NOMBRE_CAJAMADERA) {
		utilidad = new CajaMadera;
	}
	if (nombre == NOMBRE_CAJAMETAL) {
		utilidad = new CajaMetal;
	}
	if (nombre == NOMBRE_MANZANA) {
		utilidad = new Manzana;
	}
	if (nombre == NOMBRE_BANANA) {
		utilidad = new Banana;
	}
	if (nombre == NOMBRE_CEREZA) {
		utilidad = new Cereza;
	}
}

Lienzo::LienzoItem::~LienzoItem() {
	if (utilidad != NULL) {
		delete utilidad;
	}
}

/*************************************************************************/

Lienzo::LienzoLine::LienzoLine(unsigned posXinicial, unsigned posYinicial,
		unsigned posXfinal, unsigned posYfinal, enum line_color color) {
	_color = color;
	pos_x_inicial = posXinicial;
	pos_y_inicial = posYinicial;
	pos_x_final = posXfinal;
	pos_y_final = posYfinal;
}

Lienzo::LienzoLine::~LienzoLine() { }

void Lienzo::LienzoLine::draw(const Cairo::RefPtr<Cairo::Context>& contexto) {
	contexto->save();
	switch (_color) {
		case piso:
		{
			contexto->set_source_rgb(1, 0, 0);
			break;
		}
		case salida_pajaros:
		{
			contexto->set_source_rgb(1, 1, 0);
		}
	}
	contexto->move_to(pos_x_inicial, pos_y_inicial);
	contexto->line_to(pos_x_final, pos_y_final);
	contexto->set_line_width(10);
	contexto->stroke();
	contexto->restore();
}

/*************************************************************************/

Lienzo::Lienzo() {
	set_size_request(MIN_ANCHURA_NIVEL, MIN_ALTURA_NIVEL);
	drag_data_requested_for_drop = false;
	monticulo_existente = false;
	drop_item = NULL;
	fondo = Gdk::Pixbuf::create_from_file(RUTA_IMAGEN_FONDO_DEFECTO);
	set_app_paintable();
}

Lienzo::~Lienzo() {
	for (unsigned i = 0; i < items_lienzo.size(); ++i) {
		delete items_lienzo[i];
	}
	for (unsigned i = 0; i < lineas_lienzo.size(); ++i) {
		delete lineas_lienzo[i];
	}
	if (drop_item != NULL) {
		delete drop_item;
	}
}

void Lienzo::setImagenFondo(const std::string &rutaImagen) {
	fondo = Gdk::Pixbuf::create_from_file(rutaImagen);
}

void Lienzo::setAnchura(unsigned anchura) {
	set_size_request(anchura, get_height());
}

void Lienzo::setAltura(unsigned altura) {
	set_size_request(get_width(), altura);
}

void Lienzo::agregarLineaPajaros(unsigned posXinicial, unsigned posYinicial,
		unsigned posXfinal, unsigned posYfinal) {
	LienzoLine *nuevaLinea = new LienzoLine(posXinicial, posYinicial, posXfinal,
			posYfinal, LienzoLine::salida_pajaros);
	lineas_lienzo.push_back(nuevaLinea);
}

void Lienzo::agregarLineaPiso(unsigned posXinicial, unsigned posYinicial,
		unsigned posXfinal, unsigned posYfinal) {
	LienzoLine *nuevaLinea = new LienzoLine(posXinicial, posYinicial, posXfinal,
			posYfinal, LienzoLine::piso);
	lineas_lienzo.push_back(nuevaLinea);
}

void Lienzo::guardar(Escenario* escenario) {
	for (unsigned i = 0; i < items_lienzo.size(); ++i) {
		items_lienzo[i]->guardar(escenario);
	}
}

void Lienzo::cargar(Escenario* escenario) {
	LienzoItem *item;
	for (unsigned i = 0; i < escenario->getUtilidades().size(); ++i) {
		if (escenario->getUtilidades()[i]->getRol()->Nombre() != ROL_PISO) {
			item = new LienzoItem();
			item->cargar(escenario->getUtilidades()[i]);
			items_lienzo.push_back(item);
		}
	}
}

bool Lienzo::on_draw(const Cairo::RefPtr<Cairo::Context>& contexto) {
	/* Creo el area para dibujar */
	contexto->set_source_rgb(1.0, 1.0, 1.0);
	const Gtk::Allocation allocation = get_allocation();
	contexto->rectangle(0, 0, allocation.get_width(), allocation.get_height());
	contexto->fill();

	/* Re-pinto el fondo */
	Glib::RefPtr<Gdk::Pixbuf> fondoReescalado = fondo->scale_simple(get_width(),
			get_height(), Gdk::INTERP_NEAREST);
	Gdk::Cairo::set_source_pixbuf(contexto, fondoReescalado);
	contexto->paint();

	/* Re-pinto los items */
	for (unsigned i = 0; i < items_lienzo.size(); ++i) {
		items_lienzo[i]->draw(contexto, false);
	}

	/* Re-pinto las lineas */
	for (unsigned i = 0; i < lineas_lienzo.size(); ++i) {
		lineas_lienzo[i]->draw(contexto);
	}

	/* Si estoy dragueando un item y todavia no se solto el boton del mouse,
	 * mostrar una vista previa */
	if (drop_item != NULL) {
		drop_item->draw(contexto, true);
	}

	return true;
}

bool Lienzo::on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& contexto,
		int posX, int posY, guint tiempo) {
	drag_data_requested_for_drop = false;

	if (drop_item != NULL) {
		/* Actualizar la posicion */
		drop_item->setPosX(posX);
		drop_item->setPosY(posY);
		queue_draw();
		contexto->drag_status(Gdk::ACTION_COPY, tiempo);
	}
	else {
		const Glib::ustring target = drag_dest_find_target(contexto);
		if (target.empty()) {
			return false;
		}
		drag_get_data(contexto, target, tiempo);
	}
	Gtk::DrawingArea::on_drag_motion(contexto, posX, posY, tiempo);
	return true;
}

void Lienzo::on_drag_data_received(
		const Glib::RefPtr<Gdk::DragContext>& contexto, int posX, int posY,
		const Gtk::SelectionData& data, guint info, guint tiempo) {
	/* Obtengo el tool button origen de la operacion */
	Gtk::Widget* widget = drag_get_source_widget(contexto);
	Gtk::ToolPalette* drag_palette = dynamic_cast<Gtk::ToolPalette*>(widget);
	while ((widget != NULL) && (drag_palette == NULL)) {
		widget = widget->get_parent();
		drag_palette = dynamic_cast<Gtk::ToolPalette*>(widget);
	}
	Gtk::ToolItem* drag_item = NULL;
	if (drag_palette != NULL) {
		drag_item = drag_palette->get_drag_item(data);
	}
	Gtk::ToolButton* boton = dynamic_cast<Gtk::ToolButton*>(drag_item);
	if (boton == NULL) {
		return;
	}

	if (drop_item != NULL) {
		delete drop_item;
		drop_item = NULL;
	}

	LienzoItem* item = new LienzoItem(boton, posX, posY);

	if (drag_data_requested_for_drop) {
		/* Se dropeo el item. Almacenarlo y redibujar */
		items_lienzo.push_back(item);

		/* Verifico que no se agreguen 2 monticulos de huevos */
		if (boton->get_label() == NOMBRE_HUEVOS) {
			if (monticulo_existente) {
				delete item;
				items_lienzo.pop_back();
			}
			else {
				monticulo_existente = true;
			}
		}
		contexto->drag_finish(true, false, tiempo);
	}
	else {
		drop_item = item;
		contexto->drag_status(Gdk::ACTION_COPY, tiempo);
	}
	queue_draw();
	Gtk::DrawingArea::on_drag_data_received(contexto, posX, posY, data, info,
			tiempo);
}

bool Lienzo::on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& contexto,
		int /*posX*/, int /*posY*/, guint tiempo) {
	const Glib::ustring target = drag_dest_find_target(contexto);
	if (target.empty()) {
		return false;
	}
	drag_data_requested_for_drop = true;
	drag_get_data(contexto, target, tiempo);
	return true;
}

void Lienzo::on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& contexto,
		guint tiempo) {
	if (drop_item != NULL) {
		delete drop_item;
		drop_item = NULL;
		queue_draw();
		Gtk::DrawingArea::on_drag_leave(contexto, tiempo);
	}
}